import numpy

from classfile.attributes import AttributeHelper
from classfile.class_reader import ClassReader
from classfile.constant import ConstantPool, Constant, ConstantHelper
from classfile.member import Field, Method
from utils.numer_utils import math


class ClassFile:

    def __init__(self):
        # 魔数 CAFE BABY
        self.magic: numpy.uint32 = None
        # 小版本 大版本
        self.minorVersion: numpy.uint16 = None
        self.majorVersion: numpy.uint16 = None
        # 常量池
        self.constantPool: ConstantPool = None
        # 修饰符
        self.accessFlags: numpy.uint16 = None
        # 当前类的常量池索引
        self.thisClass: numpy.uint16 = None
        # 父类的常量池索引
        self.superClass: numpy.uint16 = None
        # 接口的常量池索引数组
        self.interfaces = []
        # 成员变量数组
        self.fields = []
        # 方法数组
        self.methods = []
        # 属性数组
        self.attributes = []

    def parse(self, bytes):
        cr = ClassReader(data=bytes, pos=0)
        self.readMagic(cr)
        self.readVersion(cr)
        self.readConstantPool(cr)
        self.readAccessFlags(cr)
        self.readClassName(cr)
        self.readSuperClass(cr)
        self.readInterfaces(cr)
        self.readFields(cr)
        self.readMethods(cr)
        self.readAttributes(cr)
        return self

    def readMagic(self, cr: ClassReader):
        self.magic = cr.readUInt32()

    def readVersion(self, cr: ClassReader):
        self.readMinorVersion(cr)
        self.readMajorVersion(cr)
        #print("class file minorVersion {},majorVersion :{}".format(self.minorVersion, self.majorVersion))

    def readMinorVersion(self, cr):
        self.minorVersion = cr.readUInt16()

    def readMajorVersion(self, cr):
        self.majorVersion = cr.readUInt16()

    def readConstantPool(self, cr: ClassReader):
        size = cr.readUInt16()
        cp = ConstantPool(size=size)

        def handleConstant(i, data):
            tag = data.readUInt8()
            ct = ConstantHelper.newConstant(tag=tag, cr=data)
            skip = cp.addConstant(ct)
            return skip

        cp.addConstant(None)
        math.for_skip_range(rg=range(1, size), data=cr, fn=handleConstant)

        def parseConstantValue(i, data: ConstantPool):
            ct = data.getConstant(index=i)
            ct.parseValue(data)
            skip = ct.slotCount > 1
            return skip

        math.for_skip_range(rg=range(1, size), data=cp, fn=parseConstantValue)
        self.constantPool = cp

    def readAccessFlags(self, cr):
        self.accessFlags = cr.readUInt16()

    def readClassName(self, cr):
        self.thisClass = cr.readUInt16()

    def readSuperClass(self, cr):
        self.superClass = cr.readUInt16()

    def readInterfaces(self, cr):
        size = cr.readUInt16()
        if size > 0:
            for i in range(0, size):
                self.interfaces.append(cr.readUInt16())

    def readFields(self, cr: ClassReader):
        size = cr.readUInt16()
        if size > 0:
            for i in range(0, size):
                self.fields.append(Field(cp=self.constantPool, br=cr).readAttr(cr=cr))

    def readMethods(self, cr: ClassReader):
        size = cr.readUInt16()
        if size > 0:
            for i in range(0, size):
                self.methods.append(Method(cp=self.constantPool, br=cr).readAttr(cr=cr))

    def readAttributes(self, cr: ClassReader):
        size = cr.readUInt16()
        if size > 0:
            for i in range(0, size):
                attr = AttributeHelper.newAttr(cr=cr, cp=self.constantPool)
                self.attributes.append(attr)

    def getSourceFile(self):
        for attr in self.attributes:
            if attr.attributeName.value == "SourceFile":
                return attr.sourceFile.value
        return None
